#include "cgzCLua.h"
#include "cgzCThreadPool.h"

bool cgzCLua::run(const cgzITask *iTask, cgzCMongoReply *reply)
{
    if (reply->m_err)
    {
        ocall(iTask->m_tref, "reply", {reply->m_cookid, reply->m_err});
        return true;
    }

    switch (reply->m_cmd)
    {
    case cgzCMongoCmd::AUTH:
    {
        // 认证成功
        ocall(iTask->m_tref, "auth", {"ok"});
    }
    break;
    case cgzCMongoCmd::NONE:
    {
        ocall(iTask->m_tref, "reply", {reply->m_cookid});
    }
    break;
    case cgzCMongoCmd::INSERT:
    {
        ocall(iTask->m_tref, "reply", {reply->m_cookid, reply->m_id.c_str()});
    }
    break;
    case cgzCMongoCmd::INSERTS:
    {
        ocall(iTask->m_tref, "reply", {reply->m_cookid});
    }
    break;
    case cgzCMongoCmd::DELETE_ONE:
    {
        ocall(iTask->m_tref, "reply", {reply->m_cookid, reply->m_delete});
    }
    break;
    case cgzCMongoCmd::DELETE:
    {
        ocall(iTask->m_tref, "reply", {reply->m_cookid, reply->m_delete});
    }
    break;
    case cgzCMongoCmd::FIND_ONE:
    {
        // 查询1条
        auto ret = static_cast<cgzCMongoResult *>(reply);
        ocall(iTask->m_tref, "reply", {ret->m_cookid, ret->m_result});
    }
    break;
    case cgzCMongoCmd::FIND:
    {
        // 查询多条
        auto ret = static_cast<cgzCMongoResultMany *>(reply);
        ocall(iTask->m_tref, "reply", {ret->m_cookid, ret->m_result});
    }
    break;
    default:
    {
        cgz_this_error("Unsupported command: taskNme:{}", iTask->getName());
    }
    }
    return true;
}

void cgzCLua::table2bson(bsoncxx::builder::stream::document &doc, lua_State *L)
{
    // 确保参数是一个表
    if (!lua_istable(L, -1))
    {
        throw std::runtime_error("Provided index is not a table");
    }

    // 处理非数组的情况，即常规键值对
    lua_pushnil(L); // 首先推送 nil，以便迭代表
    while (lua_next(L, -2) != 0)
    {
        // 获取键和值
        std::string key; // 键
        switch (lua_type(L, -2))
        {
        case LUA_TSTRING:
        {
            key = lua_tostring(L, -2); // 键
            break;
        }
            // 处理数字
        case LUA_TNUMBER:
        {
            if (lua_isinteger(L, -2)) // 整数
            {
                long i = lua_tointeger(L, -2);
                key = std::to_string(i); // 键
            }
            else
            {
                double d = lua_tonumber(L, -2);
                key = std::to_string(d); // 键
            }
            break;
        }
            // 处理布尔值
        case LUA_TBOOLEAN:
        {
            bool b = lua_toboolean(L, -2);
            key = b ? "true" : "false"; // 键
            break;
        }
            // 处理嵌套表
        default:
            assert(false);
        }

        switch (lua_type(L, -1))
        {
            // 处理字符串
        case LUA_TSTRING:
        {
            const char *value = lua_tostring(L, -1);
            // 特殊处理 _id 字段
            if (strcmp(key.c_str(), "_id") == 0)
            {
                doc << key << bsoncxx::oid(value);
            }
            else
            {
                // 非 _id 字段直接作为字符串
                doc << key << value;
            }
            break;
        }
            // 处理数字
        case LUA_TNUMBER:
        {
            if (lua_isinteger(L, -2)) // 整数
            {
                long value = lua_tonumber(L, -1);
                doc << key << value; // 添加到 BSON 文档
            }
            else
            {
                double value = lua_tonumber(L, -1);
                doc << key << value; // 添加到 BSON 文档
            }

            break;
        }
            // 处理布尔值
        case LUA_TBOOLEAN:
        {
            bool value = lua_toboolean(L, -1);
            doc << key << value; // 添加布尔值到 BSON 文档
            break;
        }
            // 处理嵌套表
        case LUA_TTABLE:
        {
            // 递归处理嵌套表
            bsoncxx::builder::stream::document subDoc;
            table2bson(subDoc, L);
            doc << key << subDoc.view(); // 添加嵌套文档
            break;
        }
        default:
            assert(false);
        }

        lua_pop(L, 1); // 弹出值，保留键
    }
}

void cgzCLua::bson2array(const bsoncxx::array::value &doc_arr)
{
    lua_cgz_newt(L); // 创建一个新表
    int index = 1;
    for (const auto &elem : bsoncxx::array::view(doc_arr))
    {
        if (elem.type() == bsoncxx::type::k_document)
        {
            // 处理嵌套文档
            auto sub = elem.get_document();
            bson2table(sub); // 需转换为 view
            lua_pushinteger(L, index);
            lua_settable(L, -3);
            index++;
        }
        else if (elem.type() == bsoncxx::type::k_array)
        {
            // 处理数组
            auto sub = elem.get_array();
            bson2array(bsoncxx::array::value(sub)); // 需转换为 view
            lua_pushinteger(L, index);
            lua_settable(L, -3);
            index++;
        }
        else if (elem.type() == bsoncxx::type::k_bool)
        {
            bool value = elem.get_bool();
            lua_pushboolean(L, value);
            lua_settable(L, -3);
            index++;
        }
        else if (elem.type() == bsoncxx::type::k_int32)
        {
            int value = elem.get_int32();
            lua_pushinteger(L, value);
            lua_settable(L, -3);
            index++;
        }
        else if (elem.type() == bsoncxx::type::k_int64)
        {
            long long value = elem.get_int64();
            lua_pushinteger(L, value);
            lua_settable(L, -3);
            index++;
        }
        else if (elem.type() == bsoncxx::type::k_double)
        {
            double value = elem.get_double();
            lua_pushnumber(L, value);
            lua_settable(L, -3);
            index++;
        }
        else if (elem.type() == bsoncxx::type::k_utf8)
        {
            std::string value = elem.get_utf8().value.to_string();
            lua_pushstring(L, value.c_str());
            lua_settable(L, -3);
            index++;
        }
        else
        {
            // 处理未知类型
            throw std::runtime_error("Unsupported BSON type");
        }
    }
}

void cgzCLua::bson2table(const bsoncxx::document::view &doc_view)
{
    lua_cgz_newt(L); // 创建一个新表

    for (const auto &elem : doc_view)
    {
        auto st = elem.type();
        switch (st)
        {
        case bsoncxx::type::k_oid:
        {
            // 处理 ObjectId
            auto oid = elem.get_oid();
            std::string value = oid.value.to_string();
            lua_pushstring(L, value.c_str());
            lua_setfield(L, -2, elem.key().to_string().c_str());
        }
        break;
        case bsoncxx::type::k_document:
        {
            // 处理嵌套文档
            auto sub = elem.get_document();
            bson2table(sub); // 递归调用
            lua_setfield(L, -2, elem.key().to_string().c_str());
        }
        break;
        case bsoncxx::type::k_array:
        {
            // 处理数组
            auto sub = elem.get_array();
            bson2array(bsoncxx::array::value(sub)); // 递归调用
            lua_setfield(L, -2, elem.key().to_string().c_str());
        }
        break;
        case bsoncxx::type::k_bool:
        {
            bool value = elem.get_bool();
            lua_pushboolean(L, value);
            lua_setfield(L, -2, elem.key().to_string().c_str());
        }
        break;
        case bsoncxx::type::k_int32:
        {
            int value = elem.get_int32();
            lua_pushinteger(L, value);
            lua_setfield(L, -2, elem.key().to_string().c_str());
        }
        break;
        case bsoncxx::type::k_int64:
        {

            long value = elem.get_int64();
            lua_pushinteger(L, value);
            lua_setfield(L, -2, elem.key().to_string().c_str());
        }
        break;
        case bsoncxx::type::k_double:
        {
            double value = elem.get_double();
            lua_pushnumber(L, value);
            lua_setfield(L, -2, elem.key().to_string().c_str());
        }
        break;
        case bsoncxx::type::k_utf8:
        {
            std::string value = elem.get_utf8().value.to_string();
            lua_pushstring(L, value.c_str());
            lua_setfield(L, -2, elem.key().to_string().c_str());
        }
        break;
        default:
        {
            // 处理未知类型
            throw std::runtime_error("Unsupported BSON type");
        }
        }
    }
}

static int mongo_new(lua_State *L)
{
    // 将其转换为 cgzCLua 指针
    cgzCLua *that = cgzCLua::getThat(L);
    const char *name = lua_tostring(L, 1);
    const char *ip = lua_tostring(L, 2);
    int port = lua_tointeger(L, 3);
    const char *db = lua_tostring(L, 4);
    const char *user = lua_tostring(L, 5);
    const char *password = lua_tostring(L, 6);
    int tref = luaL_ref(L, LUA_REGISTRYINDEX);
    void *node = malloc(sizeof(cgzCMongo));
    int cid = that->getId();
    auto cobj = new (node) cgzCMongo(that->m_pool, name, ip, user, password, port, db, cid);
    cobj->m_tref = tref;
    return 0;
}

static int mongo_adds(lua_State *L)
{
    // 获取上下文和参数
    cgzCLua *that = cgzCLua::getThat(L);
    long cookid = lua_tointeger(L, 1);
    const char *name = lua_tostring(L, 2);
    const char *collection = lua_tostring(L, 3);

    // 验证参数
    if (!lua_istable(L, 4))
    {
        luaL_error(L, "第四个参数必须是文档数组表");
        return 0;
    }

    // 准备文档向量
    std::vector<bsoncxx::document::value> docs;
    lua_pushnil(L); // 开始遍历表

    while (lua_next(L, 4) != 0)
    {
        // key在索引-2，value在索引-1
        if (lua_istable(L, -1))
        {
            bsoncxx::builder::stream::document doc;
            that->table2bson(doc, L);
            docs.push_back(doc.extract());
        }
        else
        {
            lua_pop(L, 1); // 弹出value，保留key供下一次迭代
            continue;
        }
        lua_pop(L, 1); // 弹出value，保留key供下一次迭代
    }

    // 创建任务
    void *node = malloc(sizeof(cgzCMongoExec));
    auto ask = new (node) cgzCMongoExec(
        cgzCMongoCmd::INSERTS,
        collection,
        docs, // 使用vector版本构造函数
        cookid);

    that->regTask(name, ask);
    return 0;
}

static int mongo_add(lua_State *L)
{
    // 将其转换为 cgzCLua 指针
    cgzCLua *that = cgzCLua::getThat(L);
    long cookid = lua_tointeger(L, 1);
    const char *name = lua_tostring(L, 2);
    const char *collect = lua_tostring(L, 3);
    bsoncxx::builder::stream::document doc;
    that->table2bson(doc, L);
    void *node = malloc(sizeof(cgzCMongoExec));
    auto ask = new (node) cgzCMongoExec(cgzCMongoCmd::INSERT, collect, doc, cookid);
    that->regTask(name, ask);
    return 0;
}

static int mongo_del(lua_State *L)
{
    // 将其转换为 cgzCLua 指针
    cgzCLua *that = cgzCLua::getThat(L);
    long cookid = lua_tointeger(L, 1);
    const char *name = lua_tostring(L, 2);
    const char *collect = lua_tostring(L, 3);
    bsoncxx::builder::stream::document doc;
    that->table2bson(doc, L);
    void *node = malloc(sizeof(cgzCMongoExec));
    auto ask = new (node) cgzCMongoExec(cgzCMongoCmd::DELETE_ONE, collect, doc, cookid);
    that->regTask(name, ask);
    return 0;
}

static int mongo_set(lua_State *L)
{
    // 将其转换为 cgzCLua 指针
    cgzCLua *that = cgzCLua::getThat(L);
    long cookid = lua_tointeger(L, 1);
    const char *name = lua_tostring(L, 2);
    const char *collect = lua_tostring(L, 3);
    bsoncxx::builder::stream::document doc;
    that->table2bson(doc, L);
    void *node = malloc(sizeof(cgzCMongoExec));
    auto ask = new (node) cgzCMongoExec(cgzCMongoCmd::UPDATE, collect, doc, cookid);
    that->regTask(name, ask);
    return 0;
}

static int mongo_get(lua_State *L)
{
    // 将其转换为 cgzCLua 指针
    cgzCLua *that = cgzCLua::getThat(L);
    long cookid = lua_tointeger(L, 1);
    const char *name = lua_tostring(L, 2);
    const char *collect = lua_tostring(L, 3);
    bsoncxx::builder::stream::document doc;
    that->table2bson(doc, L);
    void *node = malloc(sizeof(cgzCMongoExec));
    auto ask = new (node) cgzCMongoExec(cgzCMongoCmd::FIND_ONE, collect, doc, cookid);
    that->regTask(name, ask);
    return 0;
}

static int mongo_gets(lua_State *L)
{
    // 将其转换为 cgzCLua 指针
    cgzCLua *that = cgzCLua::getThat(L);
    long cookid = lua_tointeger(L, 1);
    const char *name = lua_tostring(L, 2);
    const char *collect = lua_tostring(L, 3);
    bsoncxx::builder::stream::document doc;
    that->table2bson(doc, L);
    void *node = malloc(sizeof(cgzCMongoExec));
    auto ask = new (node) cgzCMongoExec(cgzCMongoCmd::FIND, collect, doc, cookid);
    that->regTask(name, ask);
    return 0;
}

static int mongo_dels(lua_State *L)
{
    // 将其转换为 cgzCLua 指针
    cgzCLua *that = cgzCLua::getThat(L);
    long cookid = lua_tointeger(L, 1);
    const char *name = lua_tostring(L, 2);
    const char *collect = lua_tostring(L, 3);
    bsoncxx::builder::stream::document doc;
    that->table2bson(doc, L);
    void *node = malloc(sizeof(cgzCMongoExec));
    auto ask = new (node) cgzCMongoExec(cgzCMongoCmd::DELETE, collect, doc, cookid);
    that->regTask(name, ask);
    return 0;
}

static int mongo_empty(lua_State *L)
{
    // 将其转换为 cgzCLua 指针
    cgzCLua *that = cgzCLua::getThat(L);
    long cookid = lua_tointeger(L, 1);
    const char *name = lua_tostring(L, 2);
    bsoncxx::builder::stream::document doc;
    void *node = malloc(sizeof(cgzCMongoExec));
    auto ask = new (node) cgzCMongoExec(cgzCMongoCmd::NONE, "empty", doc, cookid);
    that->regTask(name, ask);
    return 0;
}

static int mongo_callback(lua_State *L)
{
    cgzCLua *that = cgzCLua::getThat(L);
    long cookid = lua_tointeger(L, 1);
    auto task = (cgzCMongoExec *)that->m_retTask.back();
    assert(task->m_cookid == cookid);
    task->m_ret = true;
    return 0;
}

int cgzCLua::luaopen_mongo(lua_State *L)
{
    static const luaL_Reg otherlib[] = {
        {"new", mongo_new},
        {"add", mongo_add},
        {"adds", mongo_adds},
        {"del", mongo_del},
        {"dels", mongo_dels},
        {"set", mongo_set},
        {"get", mongo_get},
        {"gets", mongo_gets},
        {"empty", mongo_empty},
        {"callback", mongo_callback},
        {NULL, NULL}};

    luaL_newlib(L, otherlib);
    return 1;
}